package services;

import cae.EnumStatus;
import cae.EnumVerdicts;
import cae.WindowsCAE;
import dao.SubmissionJpaController;
import dao.exceptions.NonexistentEntityException;
import entities.Contest;
import entities.Submission;
import exceptions.*;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.EnumSearchParameter;
import vo.VOContest;
import vo.VOProblem;
import vo.VOSubmission;
import vo.VOSubmissionOutput;
import vo.VOTestcase;
import vo.VOUser;

/**
 *
 * @author sbaldrich
 */
public class SubmissionService {

    private static SubmissionJpaController sc;

    public static SubmissionJpaController getInstance() {
        if (sc == null) {
            sc = new SubmissionJpaController();
        }
        return sc;
    }
    private static ContestService cs;

    public static ContestService getContestService() {
        if (cs == null) {
            cs = new ContestService();
        }
        return cs;
    }
    private static ProblemService ps;

    public static ProblemService getProblemService() {
        if (ps == null) {
            ps = new ProblemService();
        }
        return ps;
    }
    private static UserService us;

    public static UserService getUserService() {
        if (us == null) {
            us = new UserService();
        }
        return us;
    }
    private static TestcaseService ts;

    public static TestcaseService getTestcaseService() {
        if (ts == null) {
            ts = new TestcaseService();
        }
        return ts;
    }

    public void save(VOSubmission vo) throws Exception {
        sc = getInstance();
        cs = getContestService();
        ps = getProblemService();
        us = getUserService();

        Submission s = new Submission(vo);
        try {
            s.setContest(cs.findObject(vo.getContestCode()));
            s.setUser(us.findObject(vo.getUserHandle()));
            s.setProblem(ps.findObject(vo.getProblemCode()));
            sc.create(s);
        } catch (Exception e) {
            if (e instanceof NonExistentObjectException) {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("SubmissionService.save");
        }
    }

    public VOSubmission find(VOSubmission vo) throws Exception {
        sc = getInstance();
        try {
            return sc.findSubmission(vo.getIdSubmission()).getVO();
        } catch (Exception e) {
            throw e;
        }
    }

    public void update(VOSubmission vo) throws Exception {
        sc = getInstance();
        cs = getContestService();
        ps = getProblemService();
        us = getUserService();
        Submission s = new Submission(vo);
        try {
            s.setContest(cs.findObject(vo.getContestCode()));
            s.setProblem(ps.findObject(vo.getProblemCode()));
            s.setUser(us.findObject(vo.getUserHandle()));
            sc.edit(s);
        } catch (Exception e) {
            if (e instanceof NonexistentEntityException) {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("SubmissionService.update Operation");
        }
    }

    public void delete(VOSubmission vo) throws Exception {
        sc = getInstance();
        try {
            sc.destroy(vo.getIdSubmission());
        } catch (Exception e) {
            if (e instanceof NonexistentEntityException) {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("SubmissionService.delete Operation");
        }
    }

    public VOSubmissionOutput processSubmission(VOSubmission vo) throws Exception {
        ts = getTestcaseService();
        us = getUserService();

        cs = getContestService();
        if (cs.find(vo.getContestCode()).getEnd().before(vo.getDate())) {
            throw new Exception("Contest is not Active");
        }
        if (cs.findObject(vo.getContestCode()).getUserList().indexOf(us.findObject(vo.getUserHandle())) == -1) {
            throw new Exception("User is not registered in this contest");
        }


        ArrayList<VOTestcase> testcases = ts.find(vo.getProblemCode());
        try {
            VOSubmissionOutput out = judgeSubmission(vo, testcases);
            vo.setVerdict(out.getVerdict().value());
            save(vo);
            return out;
        } catch (Exception e) {
            throw e;
        }
    }

    private VOSubmissionOutput judgeSubmission(VOSubmission submission, ArrayList<VOTestcase> testCases) throws CompilationException {
        WindowsCAE cae = new WindowsCAE(3/*aquí va el time limit del problema o el problema*/, testCases, submission);
        String compileOutput = EnumStatus.NOT_COMPILED.value();
        String executionOutput = EnumStatus.NOT_COMPILED.value();
        EnumVerdicts verdict = EnumVerdicts.NOT_YET_COMPILED;
        EnumStatus status = EnumStatus.NOT_COMPILED;
        int executionTimeInMiliseconds = -1;
        cae.compileScript();
        if (cae.isSuccessfullyCompiled()) {
            cae.runTestCases();
            /*
            System.out.println("COMPILACION exitosa");
            System.out.println("COMPILATION OUTPUT:");
            System.out.println(cae.getCompilationOutput());
            System.out.println("EXECUTION OUTPUT:");
            System.out.println(cae.getExecutionOutput());
            System.out.println("VERDICT:");
            System.out.println(cae.getVerdict().value());
            System.out.println("Total Time: "+cae.getMaxExcecutionTimeInMiliSeconds()+" miliseconds");
             */

            executionOutput = cae.getExecutionOutput();
            verdict = cae.getVerdict();
            executionTimeInMiliseconds = cae.getMaxExcecutionTimeInMiliSeconds();
        } else {
            //System.out.println("COMPILATION ERROR");
            System.out.println(cae.getCompilationOutput());
        }
        try {
            WindowsCAE.killProcess("java.exe");
            WindowsCAE.killProcess("a.exe");
        } catch (Exception ex) {
            Logger.getLogger(SubmissionService.class.getName()).log(Level.SEVERE, null, ex);
        }
        status = EnumStatus.SUCCESSFULLY_JUDGED;
        compileOutput = cae.getCompilationOutput();
        return new VOSubmissionOutput(compileOutput, executionOutput, verdict, status, executionTimeInMiliseconds);
    }

    public ArrayList<VOSubmission> findAll(EnumSearchParameter parameter, Object vo) throws Exception {
        sc = getInstance();
        us = getUserService();
        ps = getProblemService();
        cs = getContestService();
        try {
            Object o = null;
            if (vo instanceof VOUser) {
                o = (Object) us.findObject(((VOUser) (vo)).getHandle());
            } else if (vo instanceof VOProblem) {
                o = (Object) ps.findObject(((VOProblem) (vo)).getProblemCode());
            } else if (vo instanceof VOContest) {
                o = (Object) cs.findObject(((VOContest) (vo)).getContestCode());
            } else if (o == null) {
                throw new NonExistentObjectException();
            }
            return sc.findAll(parameter, o);
        } catch (Exception e) {
            if (e instanceof DatabaseException) {
                throw e;
            }
            throw new InvalidParameterException();

        }
    }
}
